/* 
 * File:   main.cpp
 * Author: Lucky
 *
 * Created on 21. září 2011, 16:25
 */

#include <mpi.h>
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <time.h>

#include <iostream>
#include <fstream>
#include "Coordinate.h"
#include "Library.h"
#include "Chessboard.h"
#include "Field.h"
#include "FieldSolver.h"

#define LENGTH 10000
#define NULL 0

using namespace std;

/*
 * 
 */
int main(int argc, char** argv) {
    int my_rank;
    int p;
    int dest;
    int tag = 1;
    int source;
    int length;
    int position = 0;
    int packSize = 0;
    MPI_Status status;
    char buffer[LENGTH];
      double t1, t2;
//    int m = 100;

    /* start up MPI */
    MPI_Init(&argc, &argv);

    /* find out process rank */
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    /* find out number of processes */
    MPI_Comm_size(MPI_COMM_WORLD, &p);

    int width; //board width
    int height; //board height
    int q; //number of lit lights
    int* coords; //corrdinates array
    
    Field* field = 0; //0 is a pointer
    if (my_rank == 0) { //Procesor 0 nacte vstupy, rozdeli stacky a posle je ostatnim
        //Nacitani dat
        char * filename = argv[1];
        cout << "Nacteny soubor je: " << filename << endl << flush;
        coords = Library::LoadDataFromFile(filename, &width, &height, &q);
        cout << "Sirka " << width << " vyska: " << height << " pocet souradnic: " << q << endl << flush;
        cout<< "Pocet procesoru je: "<<p<<endl<<flush;

        MPI_Pack(&width, 1, MPI_INT, buffer, LENGTH, &packSize, MPI_COMM_WORLD);
        MPI_Pack(&height, 1, MPI_INT, buffer, LENGTH, &packSize, MPI_COMM_WORLD);
        MPI_Pack(&q, 1, MPI_INT, buffer, LENGTH, &packSize, MPI_COMM_WORLD);
        MPI_Pack(coords, 2 * q, MPI_INT, buffer, LENGTH, &packSize, MPI_COMM_WORLD);

        cout << "packsize: " << packSize << endl << flush;
    }

    //Broadcast zadani
    MPI_Bcast(&packSize, 1, MPI_INT, 0, MPI_COMM_WORLD);
    cout << "P" << my_rank << " packSize: " << packSize << endl << flush;
    MPI_Bcast(buffer, packSize, MPI_PACKED, 0, MPI_COMM_WORLD);

    if (my_rank == 0) {
        //Vytvoreni zasobniku a jeho nasledne deleni
        field = new Field(width * height);
//        cout << "P" << my_rank << " vytvoril zasobnik velikosti " << field->m_length << " a jde ho delit" << endl << flush;
        Field* tempStack = NULL;
        for (int i = 0; i < (p - 1); i++) {
            tempStack = new Field(width * height);
            if (!Library::cutStack(field, tempStack)) {
                cerr << "main: moc procaku, malo prace..." << endl << flush;
                throw "main: moc procaku, malo prace...";
            }
            
            Library::sendStack(tempStack, i+1);
//            char* bfr;
//            int pSize = 0;
//            Library::packStack(tempStack, &bfr, &pSize);
//            
////            cout << "P" << my_rank << " vypis bufferu pro procesor " << (i+1) << " ";
////            for (int i = 0; i < pSize; i++) {
////                cout << hex << (int)bfr[i];
////            }
////            cout << endl << flush;
//            
////            cout << "P" << my_rank << " posila packStack(" << pSize << ") procesoru " << (i+1) << endl << flush;
//            MPI_Send(&pSize, 1, MPI_INT, i + 1, 0, MPI_COMM_WORLD);
////            cout << "P" << my_rank << " posila stack delky " << tempStack->m_length << " procesoru " << (i+1) << endl << flush;
//            MPI_Send(bfr, pSize, MPI_PACKED, i+1, 0, MPI_COMM_WORLD);
//            delete bfr;
//            delete tempStack;
            
        }
    } else { //Ostatni procesory cekaji na praci od procesoru 0
        // rozbalovani        
        MPI_Unpack(buffer, LENGTH, &position, &width, 1, MPI_INT, MPI_COMM_WORLD);
        MPI_Unpack(buffer, LENGTH, &position, &height, 1, MPI_INT, MPI_COMM_WORLD);
        MPI_Unpack(buffer, LENGTH, &position, &q, 1, MPI_INT, MPI_COMM_WORLD);
        cout << "P" << my_rank << " prijate q= " << q << endl << flush;
        cout << "P" << my_rank << " prijate width= " << width << endl << flush;
        cout << "P" << my_rank << " prijate heigh= " << height << endl << flush;
        coords = new int[2 * q];
        MPI_Unpack(buffer, LENGTH, &position, coords, 2 * q, MPI_INT, MPI_COMM_WORLD);
        cout << "P" << my_rank << " prijate coords= " << flush;
        for (int i = 0; i < 2 * q; i++) {
            cout << coords[i] << " " << flush;
        }
        cout << endl << flush;

        bool received = Library::receiveStack(0, &field, width*height);
        if (!received) {
            cerr << "P" << my_rank << " nedostal na zactku stack!!!!" << endl;
        }
        cout << "P" << my_rank << " dostal jsem stack" << endl;
//        int pSize = 0;
//        MPI_Recv(&pSize, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
//        cout << "P" << my_rank << " RECV psize: "<< pSize << endl << flush;
//        char* bfr = new char[pSize];
//        MPI_Recv(bfr, pSize, MPI_PACKED, 0, 0, MPI_COMM_WORLD, &status);
//        
//        cout << "P" << my_rank << " vypis bufferu od procesoru 0 ";
//            for (int i = 0; i < pSize; i++) {
//                cout << hex << (int)bfr[i];
//            }
//            cout << endl << flush;
//        
//        Library::unpackStack(&field, bfr, pSize, width * height);
//        
//        cout << "P" << my_rank << " prijal stack delky" << field->m_length << endl << flush;
//        cout << "P" << my_rank << " stack: ";
//        for (int i = 0; i < field->m_length; i++) {
//            if (field->m_field[i]) {
//                cout << " 1 ";
//            } else {
//                cout << " 0 ";
//            }
//        }
//        cout << endl << flush;
//        delete bfr;
    }
    
//    cout << "P" << my_rank << " ma data, vytvari board (" << width << ", " << height << ", [" << flush;
//    for (int i = 0; i < 2*q; i++) cout << coords[i] << ", " << flush;
//    cout << "], " << q << ")" << endl << flush;

//    Chessboard* board = new Chessboard(width, height, coords, q, my_rank);
    
//    cout << "P" << my_rank << "vytvarim solver" << endl << flush; 

    //    cout << "pocet svetel: " << board->getOnLightsCount() << endl;
    //    cout << "m: " << width << endl;
    //    cout << "n: " << height << endl;
    //    cout << "q: " << q << endl;
        
    cout << "P" << my_rank << "vytvarim solver" << endl << flush; 

    FieldSolver solver(width, height, q, coords, field, p, my_rank);
    
    cout << "P" << my_rank << " solver vytvoren, poustim ho" << endl << flush; 
    MPI_Barrier(MPI_COMM_WORLD);
    t1=MPI_Wtime ();
    solver.compute();
    

    delete coords;
    // MPI ukonceni
   t2=MPI_Wtime();
    cout << "P" << my_rank << " jsem pred finalize" << endl << flush;
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Finalize();
    
    cout << "P" << my_rank << " uz jsem po finalize" << endl << flush; 
     if(my_rank==0)  printf ("Spotrebovany cas je %f.\n",t2-t1);
#ifdef _WIN32
    system("pause");
#endif
    return 0;
}

